# Copyright 2021 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

load(
    "//pw_build:pigweed.bzl",
    "pw_cc_binary",
    "pw_cc_test",
    "pw_facade",
)
load("//pw_system/py:console.bzl", "device_console", "device_simulator_console")
load("//targets/host_device_simulator:transition.bzl", "host_device_simulator_binary")
load("//targets/rp2040:flash.bzl", "flash_rp2040")
load("//targets/rp2040:transition.bzl", "rp2040_binary")

package(default_visibility = ["//visibility:public"])

licenses(["notice"])

constraint_setting(
    name = "system_example_tracing_setting",
)

constraint_value(
    name = "system_example_tracing",
    constraint_setting = ":system_example_tracing_setting",
)

cc_library(
    name = "config",
    hdrs = [
        "public/pw_system/config.h",
    ],
    visibility = ["//visibility:private"],
    deps = [":config_override"],
)

label_flag(
    name = "config_override",
    build_setting_default = ":default_config",
)

cc_library(
    name = "default_config",
    defines = [
        # TODO(b/260641850): Remove this Bazel-only default configuration once
        # pw_trace builds with Bazel.
        "PW_SYSTEM_ENABLE_TRACE_SERVICE=0",
    ],
)

cc_library(
    name = "pw_system",
    deps = [
        ":init",
        ":io",
        ":extra_platform_libs",
        ":target_hooks",
        ":work_queue",
        # pw_system has (transitive) dependencies on pw_assert and pw_log. So,
        # we add deps on the backend_impl here, saving the user from having to
        # add them manually to their cc_binary target.
        #
        # When implementing a backend for pw_assert or pw_log, *do not* depend
        # on //pw_system:pw_system. Instead, depend on the appropriate
        # component library. See :log_backend, below, for an examples.
        "//pw_assert:backend_impl",
        "//pw_log:backend_impl",
    ],
)

# Any platform-specific pw_system components. At the very least, this should
# include platform-specific initialization code. It may also include linker
# scripts.
#
# TODO: https://github.com/bazelbuild/bazel/issues/22457 - Recommend using
# @bazel_tool//tools/cpp:link_extra_libs instead, once they're not propagated
# to the exec configuration.
label_flag(
    name = "extra_platform_libs",
    build_setting_default = "//targets/host_device_simulator:boot",
)

cc_library(
    name = "log",
    srcs = [
        "log.cc",
    ],
    hdrs = [
        "pw_system_private/log.h",
    ],
    deps = [
        ":config",
        ":rpc_server",
        "//pw_log_rpc:log_service",
        "//pw_log_rpc:rpc_log_drain",
        "//pw_log_rpc:rpc_log_drain_thread",
        "//pw_multisink",
        "//pw_sync:lock_annotations",
        "//pw_sync:mutex",
    ],
)

cc_library(
    name = "log_backend",
    srcs = [
        "log_backend.cc",
    ],
    deps = [
        ":config",
        ":log",
        "//pw_bytes",
        "//pw_chrono:system_clock",
        "//pw_log:proto_utils",
        "//pw_log:pw_log.facade",
        "//pw_log_string:handler.facade",
        "//pw_log_tokenized:handler.facade",
        "//pw_log_tokenized:headers",
        "//pw_metric:global",
        "//pw_multisink",
        "//pw_result",
        "//pw_string",
        "//pw_sync:interrupt_spin_lock",
        "//pw_sync:lock_annotations",
        "//pw_tokenizer",
    ],
    # Log backends, like assert backends, generally need to be alwayslink'ed
    # because we don't inform Bazel correctly about dependencies on them. We
    # only add them as deps of binary targets, not intermediate library targets,
    # to avoid circular dependencies. But this may lead the linker to eagerly
    # remove some symbols defined here as unused.
    alwayslink = 1,
)

cc_library(
    name = "rpc_server",
    srcs = [
        "hdlc_rpc_server.cc",
    ],
    hdrs = [
        "public/pw_system/rpc_server.h",
    ],
    includes = ["public"],
    deps = [
        ":config",
        ":io",
        "//pw_assert",
        "//pw_hdlc",
        "//pw_hdlc:default_addresses",
        "//pw_hdlc:rpc_channel_output",
        "//pw_sync:mutex",
        "//pw_thread:thread_core",
        "//pw_trace",
    ],
)

cc_library(
    name = "thread_snapshot_service",
    srcs = [
        "thread_snapshot_service.cc",
    ],
    hdrs = [
        "public/pw_system/thread_snapshot_service.h",
    ],
    includes = ["public"],
    deps = [
        "//pw_rpc",
        "//pw_thread:thread_snapshot_service",
    ],
)

pw_facade(
    name = "io",
    hdrs = [
        "public/pw_system/io.h",
    ],
    backend = ":io_backend",
    includes = ["public"],
    deps = [
        "//pw_stream",
    ],
)

label_flag(
    name = "io_backend",
    build_setting_default = "//pw_system:sys_io_target_io",
)

cc_library(
    name = "init",
    srcs = [
        "init.cc",
    ],
    hdrs = [
        "public/pw_system/init.h",
    ],
    includes = ["public"],
    deps = [
        ":file_manager",
        ":log",
        ":rpc_server",
        ":target_hooks",
        ":thread_snapshot_service",
        ":transfer_service",
        ":work_queue",
        "//pw_metric:global",
        "//pw_metric:metric_service_pwpb",
        "//pw_rpc/pwpb:echo_service",
        "//pw_thread:thread",
    ] + select({
        ":system_example_tracing": [
            ":file_service",
            ":trace_service",
            "//pw_trace",
        ],
        "//conditions:default": [
        ],
    }),
)

cc_library(
    name = "work_queue",
    srcs = [
        "work_queue.cc",
    ],
    hdrs = [
        "public/pw_system/work_queue.h",
    ],
    includes = ["public"],
    deps = [
        ":config",
        "//pw_work_queue",
    ],
)

cc_library(
    name = "sys_io_target_io",
    srcs = [
        "sys_io_target_io.cc",
    ],
    includes = ["public"],
    deps = [
        ":io.facade",
        "//pw_stream",
        "//pw_stream:sys_io_stream",
    ],
)

cc_library(
    name = "socket_target_io",
    srcs = [
        "socket_target_io.cc",
    ],
    includes = ["public"],
    deps = [
        ":config",
        ":io.facade",
        "//pw_assert",
        "//pw_stream",
        "//pw_stream:socket_stream",
    ],
)

cc_library(
    name = "transfer_handlers",
    srcs = [
        "transfer_handlers.cc",
    ],
    hdrs = [
        "public/pw_system/transfer_handlers.h",
    ],
    includes = ["public"],
    deps = [
        "//pw_persistent_ram",
        "//pw_trace_tokenized:config",
        "//pw_transfer",
    ],
)

cc_library(
    name = "file_manager",
    srcs = [
        "file_manager.cc",
    ],
    hdrs = [
        "public/pw_system/file_manager.h",
    ],
    includes = ["public"],
    deps = [
        ":config",
        ":transfer_handlers",
        "//pw_file:flat_file_system",
        "//pw_persistent_ram:flat_file_system_entry",
    ] + select({
        ":system_example_tracing": [
            ":trace_service",
        ],
        "//conditions:default": [
        ],
    }),
)

cc_library(
    name = "transfer_service",
    srcs = [
        "transfer_service.cc",
    ],
    hdrs = [
        "public/pw_system/transfer_service.h",
    ],
    includes = ["public"],
    deps = [
        ":file_manager",
        "//pw_transfer",
    ],
)

cc_library(
    name = "file_service",
    srcs = [
        "file_service.cc",
    ],
    hdrs = [
        "public/pw_system/file_service.h",
    ],
    includes = ["public"],
    deps = [
        ":file_manager",
    ],
)

cc_library(
    name = "trace_service",
    srcs = [
        "trace_service.cc",
    ],
    hdrs = [
        "public/pw_system/trace_service.h",
    ],
    includes = ["public"],
    deps = [
        ":transfer_handlers",
        "//pw_persistent_ram",
        "//pw_trace_tokenized:trace_service_pwpb",
    ],
)

cc_library(
    name = "target_hooks",
    hdrs = [
        "public/pw_system/target_hooks.h",
    ],
    includes = ["public"],
    deps = [
        ":target_hooks_backend",
        "//pw_thread:thread",
    ],
)

label_flag(
    name = "target_hooks_backend",
    build_setting_default = ":target_hooks_multiplexer",
)

# This isn't the best solution, but it's close enough for now. Target hooks are
# not generically related to an OS, and should be inject-able by downstream
# projects. For now, assume the pre-baked OS-specific hooks are good enough.
cc_library(
    name = "target_hooks_multiplexer",
    visibility = ["//targets:__pkg__"],
    deps = select({
        "//pw_build/constraints/rtos:freertos": [":freertos_target_hooks"],
        "//conditions:default": [":stl_target_hooks"],
    }),
)

cc_library(
    name = "stl_target_hooks",
    srcs = [
        "stl_target_hooks.cc",
    ],
    includes = ["public"],
    deps = [
        ":config",
        "//pw_thread:thread",
        "//pw_thread_stl:thread",
    ],
)

cc_library(
    name = "freertos_target_hooks",
    srcs = [
        "freertos_target_hooks.cc",
    ],
    hdrs = [
        "public/pw_system/target_hooks.h",
    ],
    includes = ["public"],
    target_compatible_with = [
        "//pw_build/constraints/rtos:freertos",
    ],
    deps = [
        ":config",
        "//pw_thread:thread",
        "//pw_thread_freertos:thread",
    ],
)

pw_cc_binary(
    name = "system_example",
    # This is marked as testonly because the example app pulls in the RPC unit
    # test runner. In a real production binary, you wouldn't want to have any
    # testonly dependencies.
    testonly = True,
    srcs = ["example_user_app_init.cc"],
    deps = [
        ":pw_system",
        "//pw_unit_test:rpc_service",
    ],
)

cc_library(
    name = "async",
    srcs = [
        "pw_system_private/threads.h",
        "system.cc",
        "threads.cc",
    ],
    hdrs = ["public/pw_system/system.h"],
    implementation_deps = [
        ":async_packet_io",
        ":file_manager",
        ":file_service",
        ":log",
        ":thread_snapshot_service",
        ":transfer_service",
        ":work_queue",
        "//pw_allocator:best_fit_block_allocator",
        "//pw_async2:allocate_task",
        "//pw_async2:pend_func_task",
        "//pw_hdlc:router",
        "//pw_multibuf:simple_allocator",
        "//pw_rpc/pwpb:echo_service",
        "//pw_thread:thread",
    ],
    includes = ["public"],
    deps = [
        "//pw_allocator:allocator",
        "//pw_async2:dispatcher",
        "//pw_channel",
        "//pw_rpc",
    ],
)

cc_library(
    name = "async_packet_io",
    srcs = ["async_packet_io.cc"],
    hdrs = ["public/pw_system/internal/async_packet_io.h"],
    implementation_deps = [
        "//pw_assert",
        "//pw_log",
    ],
    includes = ["public"],
    visibility = ["//visibility:private"],
    deps = [
        ":config",
        "//pw_allocator:allocator",
        "//pw_async2:dispatcher",
        "//pw_channel",
        "//pw_channel:forwarding_channel",
        "//pw_containers:inline_var_len_entry_queue",
        "//pw_hdlc:router",
        "//pw_multibuf",
        "//pw_multibuf:simple_allocator",
        "//pw_rpc",
        "//pw_sync:lock_annotations",
        "//pw_sync:mutex",
        "//pw_sync:thread_notification",
        "//pw_thread:thread",
        "//pw_thread:thread_core",
    ],
)

pw_cc_test(
    name = "async_packet_io_test",
    srcs = ["async_packet_io_test.cc"],
    deps = [
        ":async_packet_io",
        "//pw_allocator:testing",
        "//pw_channel:loopback_channel",
        "//pw_multibuf:testing",
    ],
)

pw_cc_test(
    name = "system_async_test",
    srcs = ["system_async_test.cc"],
    deps = [
        ":async",
        "//pw_allocator:testing",
        "//pw_channel:loopback_channel",
        "//pw_multibuf:testing",
    ],
)

pw_cc_binary(
    name = "system_async_host_example",
    testonly = True,
    srcs = ["system_async_host_example.cc"],
    deps = [
        ":async",
        "//pw_channel:epoll_channel",
        "//pw_multibuf:testing",
    ],
)

host_device_simulator_binary(
    name = "system_async_host_simulator_example",
    testonly = True,
    binary = ":system_async_host_example",
)

host_device_simulator_binary(
    name = "simulator_system_example",
    testonly = True,
    binary = ":system_example",
)

rp2040_binary(
    name = "rp2040_system_example",
    testonly = True,
    binary = ":system_example",
)

flash_rp2040(
    name = "flash_rp2040_system_example",
    testonly = True,
    rp2040_binary = ":rp2040_system_example",
)

# Start :simulator_system_example and connect to it with pw console.
device_simulator_console(
    name = "simulator_system_example_console",
    testonly = True,
    host_binary = ":simulator_system_example",
    script = "//pw_system/py:device_sim",
)

# Conect to a device running :system_example over serial with the
# pw-system-console.
device_console(
    name = "system_example_console",
    testonly = True,
    binary = ":system_example",
    script = "//pw_system/py:pw_system_console",
)
